package gspringboot

import (
	"errors"
	"gitee.com/hongzhaomin/gspring/annotation"
	"gitee.com/hongzhaomin/gspring/iface"
	"gitee.com/hongzhaomin/hzm-common-go/assert"
	"os"
	"sync"
)

const (
	ApplicationConfigPropertiesName = "applicationConfig" // app配置文件
	CommandLineArgsPropertiesName   = "commandLineArgs"   // 命令行参数
	SystemEnvArgsPropertiesName     = "systemEnvArgs"     // 系统环境变量
	LocalPropertiesName             = "localStore"        // 本地内存配置
)

// 数据源优先级：配置文件 > 命令行参数 > 系统环境 > 本地
var sourceNames = []string{
	ApplicationConfigPropertiesName,
	CommandLineArgsPropertiesName,
	SystemEnvArgsPropertiesName,
	LocalPropertiesName,
}

type ConfigurationEnvironment interface {
	iface.Environment

	// AddActiveProfiles 添加当前激活的配置文件名称简介
	AddActiveProfiles(profiles ...string)

	// GetPropertiesSource 根据 name 获取配置源对象
	GetPropertiesSource(name string) PropertiesSource

	// GetStringReplacePlaceholders 根据 key 获取配置值并返回，
	// 如果拿到的value配置值中含有引用环境变量的占位符: ${xxx}, 会从环境中替换成对应的值再返回
	GetStringReplacePlaceholders(key string) string

	// Set  设置 key 的值 value
	Set(key string, value any)
}

func newDefaultEnvironment() *DefaultEnvironment {
	environment := &DefaultEnvironment{
		activeProfiles:      make(map[string]struct{}, 8),
		propertiesSourceMap: make(map[string]PropertiesSource, 4),
		mu:                  new(sync.Mutex),
	}
	environment.addPropertiesSource(LocalPropertiesName, NewLocalPropertiesSource())
	return environment
}

var _ ConfigurationEnvironment = (*DefaultEnvironment)(nil)

// DefaultEnvironment 默认环境对象
// 数据源分为本地，系统环境，命令行参数和配置文件四种：
// 优先级依次为：配置文件 > 命令行参数 > 系统环境 > 本地
type DefaultEnvironment struct {
	iface.Environment
	annotation.Component

	activeProfiles      map[string]struct{}
	propertiesSourceMap map[string]PropertiesSource
	mu                  *sync.Mutex
}

func (my *DefaultEnvironment) addPropertiesSource(sourceName string, source PropertiesSource) {
	my.mu.Lock()
	defer my.mu.Unlock()
	if _, ok := my.propertiesSourceMap[sourceName]; ok {
		panic(errors.New("properties source is exist: " + sourceName))
	}
	my.propertiesSourceMap[sourceName] = source
}

func (my *DefaultEnvironment) AddActiveProfiles(profiles ...string) {
	my.mu.Lock()
	os.Environ()
	defer my.mu.Unlock()
	for _, profile := range profiles {
		my.activeProfiles[profile] = struct{}{}
	}
}

func (my *DefaultEnvironment) GetPropertiesSource(name string) PropertiesSource {
	return my.propertiesSourceMap[name]
}

func (my *DefaultEnvironment) GetStringReplacePlaceholders(key string) string {
	return getStringReplacePlaceholders(key, my)
}

// GetActiveProfiles 获取当前激活的配置文件名称简介
// for example: application-dev.yaml 中的 dev
func (my *DefaultEnvironment) GetActiveProfiles() []string {
	activeProfiles := make([]string, 0, len(my.activeProfiles))
	for profile := range my.activeProfiles {
		activeProfiles = append(activeProfiles, profile)
	}
	return activeProfiles
}

// ContainsKey 判断key是否存在
func (my *DefaultEnvironment) ContainsKey(key string) bool {
	for _, sourceName := range sourceNames {
		source, ok := my.propertiesSourceMap[sourceName]
		if ok {
			if source.ContainsKey(key) {
				return true
			}
		}
	}
	return false
}

// Set 设置 key 的值 value
func (my *DefaultEnvironment) Set(key string, value any) {
	assert.NotBlank(key, "environment set key is blank")
	assert.NonNil(value, "environment set value is empty")
	my.mu.Lock()
	defer my.mu.Unlock()
	localSource := my.propertiesSourceMap[LocalPropertiesName].(*LocalPropertiesSource)
	localSource.set(key, value)
}

// Get 根据key获取接口类型的配置值
func (my *DefaultEnvironment) Get(key string) any {
	for _, sourceName := range sourceNames {
		source, ok := my.propertiesSourceMap[sourceName]
		if ok && source.ContainsKey(key) {
			return source.Get(key)
		}
	}
	return nil
}

// GetString 根据key获取string类型的配置值
func (my *DefaultEnvironment) GetString(key string) string {
	for _, sourceName := range sourceNames {
		source, ok := my.propertiesSourceMap[sourceName]
		if ok && source.ContainsKey(key) {
			return source.GetString(key)
		}
	}
	return ""
}

// GetInt 根据key获取int类型的配置值
func (my *DefaultEnvironment) GetInt(key string) int {
	for _, sourceName := range sourceNames {
		source, ok := my.propertiesSourceMap[sourceName]
		if ok && source.ContainsKey(key) {
			return source.GetInt(key)
		}
	}
	return 0
}

// GetInt64 根据key获取int64类型的配置值
func (my *DefaultEnvironment) GetInt64(key string) int64 {
	for _, sourceName := range sourceNames {
		source, ok := my.propertiesSourceMap[sourceName]
		if ok && source.ContainsKey(key) {
			return source.GetInt64(key)
		}
	}
	return 0
}

// GetFloat64 根据key获取float64类型的配置值
func (my *DefaultEnvironment) GetFloat64(key string) float64 {
	for _, sourceName := range sourceNames {
		source, ok := my.propertiesSourceMap[sourceName]
		if ok && source.ContainsKey(key) {
			return source.GetFloat64(key)
		}
	}
	return 0
}

// GetBool 根据key获取bool类型的配置值
func (my *DefaultEnvironment) GetBool(key string) bool {
	for _, sourceName := range sourceNames {
		source, ok := my.propertiesSourceMap[sourceName]
		if ok && source.ContainsKey(key) {
			return source.GetBool(key)
		}
	}
	return false
}
